<?php

namespace app\admin\logic;

use app\admin\model\Attachment as AttachmentModel;
use think\File;
use think\file\UploadedFile;


/**
 * 文件上传
 *
 * * 所有文件上传尽量通过该类操作
 *
 * @package back\app\backend\common\logic
 * Author: zsw
 */
class Uploader
{
    private $config = [
        'ext' => [
            'image' => ['png','gif','jpg','jpeg','bmp'],
            'flash' => ['swf', 'flv'],
            'media' => ['swf', 'flv', 'mp3', 'wav', 'wma', 'wmv', 'mid', 'avi', 'mpg', 'asf', 'rm', 'rmvb', 'mp4'],
            'file'  => ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'htm', 'html', 'txt', 'zip', 'rar', 'gz', 'bz2'],
        ],
        'path' => '/uploads/{year}{mon}{day}/{uuid}{.suffix}', //
        'maxSize' => '10mb', // 文件上传大小（b,k|kb,m|mb,g|gb）
    ];

    public function __construct($config = [])
    {
        $this->config = array_merge($this->config, $config);
    }

    public function getExt(?string $key = null)
    {
        return $key ? $this->config['ext'][$key] ?? [] : $this->config['ext'];
    }

    /**
     * 检验文件是否存在
     * @param File $file
     *
     * @return bool
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    private function checkFileExist(File $file)
    {
        return AttachmentModel::where('uuid', '=', $file->hash())->find();
    }

    /**
     * 保存数据
     *
     * @param array $data
     *
     * @return AttachmentModel|\think\Model
     */
    public function saveData( array $data ){
        return AttachmentModel::create($data);
    }

    /**
     * 文件上传类
     *
     * @param File|string  $file    File文件|base64
     * @param array  $params        覆盖数据
     * @param string $fileType      数据格式 后台upload_file_ext配置
     * @param bool $save      存入数据量
     *
     * @return array
     * Author: zsw iszsw@qq.com
     */
    public function handle($file, $params = [], $fileType = 'image', $save = true)
    {
        if (!$file) {
            throw new \Exception(['data', 'Not exist']);
        }

        $file_ext = $this->config['ext'];
        if ($fileType && !array_key_exists($fileType, $file_ext)) {
            throw new \Exception('Unsupported data format');
        }

        if (is_string($file)) {
            $file = static::saveBase64Image($file);
        }

        if (!$file instanceof File) {
            throw new \Exception(['upload', 'fail']);
        }

        // 校验文件存在
        if ($fileModel = $this->checkFileExist($file)) {
//            return $fileModel->domain . $fileModel->url;
        }

        preg_match('/(\d+)(\w+)/', $this->config['maxSize'], $matches);
        $type = strtolower($matches[2]);
        $typeDict = ['b' => 0, 'k' => 1, 'kb' => 1, 'm' => 2, 'mb' => 2, 'gb' => 3, 'g' => 3];
        $fileSize = (int)$matches[1] * pow(1024, isset($typeDict[$type]) ? $typeDict[$type] : 0);
        $fileValidate = [];
        if ($fileSize > 0) {
            $fileValidate[] = "filesize:{$fileSize}";
        }
        if (isset($file_ext[$fileType])) {
            $fileValidate[] = "fileExt:" . implode(',', $file_ext[$fileType]);
        }
        validate(['file' => implode('|', $fileValidate)])->check(['file' => $file]);
        $uuid = $file->hash();
        $tempPath = $file->getRealPath();
        $originalName = $file->getOriginalName();
        $suffix = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
        $suffix = $suffix ? $suffix : 'tmp';
        $replaceArr = [
            '{year}'     => date("Y"),
            '{mon}'      => date("m"),
            '{day}'      => date("d"),
            '{hour}'     => date("H"),
            '{min}'      => date("i"),
            '{sec}'      => date("s"),
            '{random}'   => getRandom(16),
            '{random32}' => getRandom(32),
            '{filename}' => $suffix ? substr($originalName, 0, strripos($originalName, '.')) : $originalName,
            '{suffix}'   => $suffix,
            '{.suffix}'  => $suffix ? '.'.$suffix : '',
            '{md5}'      => md5_file($tempPath),
            '{uuid}'     => $uuid,
        ];
        $url = str_replace(array_keys($replaceArr), array_values($replaceArr), $this->config['path']);
        $uploadDir = dirname($url);
        $fileName = basename($url);
        $fileSaveName = index_path($uploadDir) . $fileName;
        if (!static::moveFile($tempPath, $fileSaveName, true)) {
            throw new \Exception(['upload', 'fail']);
        }
        $width = $height = 0;
        $fileSize = filesize($fileSaveName);
        $mime = $file->getOriginalMime();

        // 图片获取宽高
        if (in_array($suffix, $file_ext['image']))
        {
            $imgInfo = getimagesize($fileSaveName);
            $width = isset($imgInfo[0]) ? $imgInfo[0] : $width;
            $height = isset($imgInfo[1]) ? $imgInfo[1] : $height;
        }

        // 保存数据库
        if ($save) {
            $fileInfo = [
                'uuid'        => $uuid,
                'uploader'    => $params['uploader'] ?? '',
                'url'         => $url,
                'name'        => $originalName,
                'size'        => $fileSize,
                'mime'        => $mime,
                'width'       => $width,
                'height'      => $height,
                'suffix'      => $suffix,
                'ip'          => request()->ip(),
                'domain'      => '',
            ];
            $this->saveData($fileInfo);
        }

        return $url;
    }

    private static function saveBase64Image($base64_image){
        if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $base64_image, $result)){
            //图片后缀
            $type = $result[2];
            //保存位置--图片名
            $image_name= getRandom(16).".".$type;
            $image_file = runtime_path() . 'temp';
            $image_real_url = $image_file.'/'.$image_name;
            if (!file_exists($image_file)){
                mkdir($image_file, 0700);
                fopen($image_file.'\\'.$image_name, "w");
            }
            //解码
            $decode=base64_decode(str_replace($result[1], '', $base64_image));
            if (file_put_contents($image_real_url, $decode)){
                return (new UploadedFile($image_real_url, $image_name, 'image/'.$type));
            }
        }
        return false;
    }

    /**
     * 移动文件
     * @param      $old
     * @param      $new
     * @param bool $cover 覆盖
     *
     * @return bool
     */
    public static function moveFile($old, $new, $cover = false)
    {
        if(static::copyFile($old, $new, $cover)){
            @unlink($old);
            return true;
        }
        return false;
    }


    /**
     * 复制文件
     * @param      $old
     * @param      $new
     * @param bool $cover
     *
     * @return bool
     */
    private static function copyFile($old, $new, $cover = false)
    {
        if (!is_file($old)) {
            return false;
        }
        if (!$cover && file_exists($new)) {
            return false;
        }
        $path = dirname($new);
        if (!is_dir($path) && !mkdir($path, 0755, true)) {
            return false;
        }
        $handle1 = fopen($old, "r");
        $handle2 = fopen($new, "w");
        stream_copy_to_stream($handle1, $handle2);
        fclose($handle1);
        fclose($handle2);
        return true;
    }

}
